package com.boarsoft.sofa.util;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import com.alipay.hessian.generic.exception.ConvertException;
import com.alipay.hessian.generic.model.GenericArray;
import com.alipay.hessian.generic.model.GenericClass;
import com.alipay.hessian.generic.model.GenericCollection;
import com.alipay.hessian.generic.model.GenericMap;
import com.alipay.hessian.generic.model.GenericObject;

/**
 * SOFA泛化返回对象转换工具
 *
 *
 */
public class GenericObjectUtil {
	/**
	 * 将 GenericObject 转换为具体对象
	 *
	 * @param genericObject
	 *            待转换的GenericObject
	 * @return 转换后结果
	 */
	@SuppressWarnings("unchecked")
	public static <T> T convertToObject(Object genericObject) {

		try {
			return (T) innerToConvertObject(genericObject, new IdentityHashMap<Object, Object>());
		} catch (Throwable t) {
			throw new ConvertException(t);
		}
	}

	private static Object innerToConvertObject(Object value, Map<Object, Object> map) throws Exception {

		// 判null
		if (value == null) {
			return null;
		}

		// 值为GenericObject类型
		if (value.getClass() == GenericObject.class) {
			GenericObject genericObject = (GenericObject) value;
			return doConvertToObject(genericObject, map);
		}

		// 值为GenericCollection类型
		if (value.getClass() == GenericCollection.class) {
			GenericCollection collection = (GenericCollection) value;
			return doConvertToCollection(collection, map);
		}

		// 值为GenericMap类型
		if (value.getClass() == GenericMap.class) {
			GenericMap genericMap = (GenericMap) value;
			return doConvertToMap(genericMap, map);
		}

		// 值为GenericArray类型
		if (value.getClass() == GenericArray.class) {
			GenericArray genericArray = (GenericArray) value;
			return doConvertToArray(genericArray, map);
		}

		// 值为GenericClass类型
		if (value.getClass() == GenericClass.class) {
			GenericClass genericClass = (GenericClass) value;
			return doConvertToClass(genericClass, map);
		}

		// 说明是jdk类, 处理集合类,将集合类中结果转换
		Object obj = handleCollectionOrMapToObject(value, map);
		return obj;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Object handleCollectionOrMapToObject(Object value, Map<Object, Object> map) throws Exception {

		if (value == null) {
			return null;
		}

		// 查看缓存的转换记录是否存在转换历史
		if (map.get(value) != null) {
			return map.get(value);
		}

		// 处理Collection实现类情况
		if (Collection.class.isAssignableFrom(value.getClass())) {

			Collection values = (Collection) value;
			Collection result = (Collection) value.getClass().newInstance();
			map.put(value, result);

			for (Object obj : values) {
				result.add(innerToConvertObject(obj, map));
			}

			return result;
		}

		// 处理Map实现类情况
		if (Map.class.isAssignableFrom(value.getClass())) {

			Map<Object, Object> valueMap = (Map<Object, Object>) value;
			Map result = (Map) value.getClass().newInstance();
			map.put(value, result);

			Iterator iter = valueMap.entrySet().iterator();
			while (iter.hasNext()) {
				Entry entry = (Entry) iter.next();
				result.put(innerToConvertObject(entry.getKey(), map), innerToConvertObject(entry.getValue(), map));
			}

			return result;
		}

		return value;
	}

	private static Object doConvertToObject(GenericObject genericObject, Map<Object, Object> map) throws Exception {

		if (genericObject == null) {
			return genericObject;
		}
		Map<String, Object> result = new HashMap<String, Object>();

		// 如果map中缓存转换结果,直接返回
		Object object = map.get(genericObject);
		if (object != null) {
			return object;
		}

		for (Entry<String, Object> entry : genericObject.getFields().entrySet()) {
			result.put(entry.getKey(), convertToObject(entry.getValue()));
		}

		return result;
	}

	private static Class<? extends Object> loadClassFromTCCL(String clazzName) throws ClassNotFoundException {
		return Class.forName(clazzName, true, Thread.currentThread().getContextClassLoader());
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Object doConvertToCollection(GenericCollection genericCollection, Map<Object, Object> map) throws Exception {

		// 如果map中缓存转换结果,直接返回
		Object object = map.get(genericCollection);
		if (object != null) {
			return object;
		}

		// 检测 GenericCollection 是否封装 Collection 实例
		Class clazz = loadClassFromTCCL(genericCollection.getType());
		if (!Collection.class.isAssignableFrom(clazz)) {
			throw new IllegalArgumentException("GenericCollection实例未封装Collection实例.");
		}

		// 初始化Collection对象,并放入map
		Collection result = (Collection) clazz.newInstance();
		map.put(genericCollection, result);

		// 填充Collection对象
		Collection values = genericCollection.getCollection();
		for (Object value : values) {
			result.add(innerToConvertObject(value, map));
		}

		return result;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static Object doConvertToMap(GenericMap genericMap, Map<Object, Object> map) throws Exception {

		// 如果map中缓存转换结果,直接返回
		Object object = map.get(genericMap);
		if (object != null) {
			return object;
		}

		// 检测 GenericMap 是否封装 Map 实例
		Class clazz = loadClassFromTCCL(genericMap.getType());
		if (!Map.class.isAssignableFrom(clazz)) {
			throw new IllegalArgumentException("GenericMap实例未封装Map实例.");
		}

		// 初始化对象,并放入map
		Map result = (Map) clazz.newInstance();
		map.put(genericMap, result);

		// 填充map对象
		Iterator iter = genericMap.getMap().entrySet().iterator();
		while (iter.hasNext()) {
			Entry entry = (Entry) iter.next();
			result.put(innerToConvertObject(entry.getKey(), map), innerToConvertObject(entry.getValue(), map));
		}

		return result;
	}

	@SuppressWarnings("rawtypes")
	private static Object doConvertToArray(GenericArray genericArray, Map<Object, Object> map) throws Exception {

		// 如果map中缓存转换结果,直接返回
		Object object = map.get(genericArray);
		if (object != null) {
			return object;
		}

		// 初始化数组对象,并放入map
		Class clazz = loadClassFromTCCL(genericArray.getComponentType());
		Object[] objects = genericArray.getObjects();
		Object result = Array.newInstance(clazz, objects.length);
		map.put(genericArray, result);

		// 填充数组对象
		for (int i = 0; i < objects.length; i++) {
			Array.set(result, i, innerToConvertObject(objects[i], map));
		}

		return result;
	}

	private static Object doConvertToClass(GenericClass genericClass, Map<Object, Object> map) throws ClassNotFoundException {
		// 如果map中缓存转换结果,直接返回
		Object object = map.get(genericClass);
		if (object != null) {
			return object;
		}

		Object obj = loadClassFromTCCL(genericClass.getClazzName());
		map.put(genericClass, obj);
		return obj;
	}

	@SuppressWarnings("rawtypes")
	public static GenericObject buildGenericObject(String className, Map data) {
		GenericObject object = new GenericObject(className);
		Iterator iterator = data.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry entry = (Entry) iterator.next();
			object.putField((String) entry.getKey(), entry.getValue());
		}
		return object;
	}
}